package com.idanchuang.component.businesslog.es.runner;

import com.idanchuang.component.businesslog.es.annotations.*;
import com.idanchuang.component.businesslog.es.config.BusinessLogConfig;
import com.idanchuang.component.businesslog.es.util.ScanJar;
import com.idanchuang.component.businesslog.es.util.ScanPackage;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.AcknowledgedResponse;
import org.elasticsearch.client.indexlifecycle.*;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.common.unit.TimeValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 初始化索引配置
 *
 * @author yjy
 * 2020-02-21 15:53
 */
public class InitBusinessLogRunner implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(InitBusinessLogRunner.class);

    private RestHighLevelClient client;
    private BusinessLogConfig businessLogConfig;
    private IndexLifeCycle indexLifeCycle;

    public InitBusinessLogRunner(RestHighLevelClient restHighLevelClient, BusinessLogConfig businessLogConfig) {
        this.client = restHighLevelClient;
        this.businessLogConfig = businessLogConfig;
        if (businessLogConfig.isIndexPolicy()) {
            this.indexLifeCycle = new XPackIndexLifeCycle(restHighLevelClient);
        } else {
            this.indexLifeCycle = new OpenDistroIndexLifeCycle(restHighLevelClient);
        }
    }

    @Override
    public void run(String... args) throws Exception {
        String packageName = businessLogConfig.getDocumentPackage();
        Set<Class<?>> classes = ScanPackage.getClassesByPackageName(packageName);
        classes.addAll(ScanJar.getClassesByPackageName(packageName));

        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(Document.class)) {
                Document document = clazz.getAnnotation(Document.class);
                String policyName = indexLifeCycle.findIlmPolicy(document);
                if (policyName != null) {
                    indexLifeCycle.checkTemplate(document.indexName(), policyName);
                    checkIndex(document.indexName());
                }
            }
        }
    }

    /**
     * 寻找策略, 不存在则创建策略
     * @param document 相关配置
     * @return 策略名
     * @throws IOException e
     */
    private String findIlmPolicy(Document document) throws IOException {
        String rollMode = document.rollMode().toString();
        String maxAge = document.maxDays() <= 0 ? "0d" : document.maxDays() + "d";
        // 不执行策略
        if (document.rollMode() == RollMode.NONE && document.maxDays() <= 0) {
            return null;
        }
        // 执行策略
        String policyName = ("policy_ro_" + rollMode + "_del_" + maxAge).toLowerCase();
        GetLifecyclePolicyRequest request = new GetLifecyclePolicyRequest(policyName);
        try {
            client.indexLifecycle().getLifecyclePolicy(request, RequestOptions.DEFAULT);
        } catch (ElasticsearchStatusException e) {
            if (e.getMessage().contains("resource_not_found_exception")) {
                Map<String, Phase> map = new HashMap<>();

                TimeValue timeValue;
                switch (document.rollMode()) {
                    case MINUTE:
                        timeValue = TimeValue.parseTimeValue("1m", "1m");
                        break;
                    case HOUR:
                        timeValue = TimeValue.parseTimeValue("1h", "1h");
                        break;
                    case DAY:
                        timeValue = TimeValue.parseTimeValue("1d", "1d");
                        break;
                    case WEEK:
                        timeValue = TimeValue.parseTimeValue("7d", "7d");
                        break;
                    case MONTH:
                        timeValue = TimeValue.parseTimeValue("30d", "30d");
                        break;
                    case YEAR:
                        timeValue = TimeValue.parseTimeValue("365d", "365d");
                        break;
                    default:
                        timeValue = null;
                        break;
                }
                if (timeValue != null) {
                    Map<String, LifecycleAction> lifecycleActionMap1 = new HashMap<>();
                    LifecycleAction rollover = new RolloverAction(null, timeValue, null);
                    lifecycleActionMap1.put("rollover", rollover);
                    map.put("hot", new Phase("hot", null, lifecycleActionMap1));
                }
                if (document.maxDays() > 0) {
                    Map<String, LifecycleAction> lifecycleActionMap2 = new HashMap<>();
                    LifecycleAction delete = new DeleteAction();
                    lifecycleActionMap2.put("delete", delete);
                    map.put("delete", new Phase("delete", TimeValue.parseTimeValue(maxAge, maxAge), lifecycleActionMap2));
                }
                LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policyName, map);
                PutLifecyclePolicyRequest putRequest = new PutLifecyclePolicyRequest(lifecyclePolicy);
                AcknowledgedResponse response = client.indexLifecycle().putLifecyclePolicy(putRequest, RequestOptions.DEFAULT);
                boolean acknowledged = response.isAcknowledged();
                if (acknowledged) {
                    log.info("create lifecyclePolicy {}", policyName);
                }
            }
        }
        return policyName;
    }

    /**
     * 检查索引模版, 不存在则初始化
     * @param indexName 索引
     * @param policyName 策略
     * @throws IOException e
     */
    private void checkTemplate(String indexName, String policyName) throws IOException {
        String templateName = indexName + "_template";
        IndexTemplatesExistRequest request = new IndexTemplatesExistRequest(templateName);
        boolean exists = client.indices().existsTemplate(request, RequestOptions.DEFAULT);
        if (!exists) {
            PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest(templateName);
            Map<String, Object> settings = new HashMap<>();
            settings.put("number_of_shards", 1);
            settings.put("number_of_replicas", 1);
            settings.put("index.lifecycle.name", policyName);
            settings.put("index.lifecycle.rollover_alias", indexName);
            putRequest.settings(settings);
            putRequest.patterns(Collections.singletonList(indexName + "-*"));
            org.elasticsearch.action.support.master.AcknowledgedResponse response = client.indices()
                    .putTemplate(putRequest, RequestOptions.DEFAULT);
            boolean acknowledged = response.isAcknowledged();
            if (acknowledged) {
                log.info("create index template {}", templateName);
            }
        }
    }

    /**
     * 检查索引, 不存在则初始化
     * @param indexName 索引
     * @throws IOException e
     */
    private void checkIndex(String indexName) throws IOException {
        GetIndexRequest request = new GetIndexRequest(indexName + "-*");
        if (!client.indices().exists(request, RequestOptions.DEFAULT)) {
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName + "-000001");
            Map<String, Object> aliases0 = new HashMap<>();
            aliases0.put("is_write_index", true);
            Map<String, Object> aliases = new HashMap<>();
            aliases.put(indexName, aliases0);
            createIndexRequest.aliases(aliases);
            try {
                CreateIndexResponse response = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
                boolean acknowledged = response.isAcknowledged();
                if (acknowledged) {
                    log.info("create index {}", response.index());
                }
            } catch (IOException e) {
                throw new RuntimeException("Error for creating index: " + request.toString(), e);
            }
        }
    }

}
